const common = @import("common.zig");
const memory = @import("memory.zig");
const std = @import("std");

const ValueArray = @import("value.zig").ValueArray;
const Value = @import("value.zig").Value;
const print_value = @import("value.zig").print_value;

pub const OpCode = enum {
    OP_CONSTANT,
    OP_RETURN,
    SOME_OTHER,
};

pub const Chunk = struct {
    // capacity包含在切片中

    code: ?[]u8,
    count: u32,
    constants: ValueArray,

    pub fn init() Chunk {
        return .{
            .code = null,
            .count = 0,
            .constants = ValueArray.init(),
        };
    }

    pub fn deinit(self: *Chunk) void {
        self.constants.deinit();
        memory.free_array(self.code.?);
        self.count = 0;
        self.code = null;
    }

    fn grow_self(self: *Chunk) void {
        if (self.code) |code| {
            if (self.count + 1 > code.len) {
                self.code = memory.grow_array(u8, code, memory.grow_capacity(code.len));
            }
        } else {
            self.code = memory.grow_array(u8, null, 8);
        }
    }

    pub fn write_chunk(self: *Chunk, byte: u8) void {
        self.grow_self();

        self.code.?[self.count] = byte;
        self.count += 1;
    }

    pub fn disassemble_chunk(self: *const Chunk, name: []const u8) void {
        common.stdout.print("== {s} ==\n", .{name}) catch {
            @panic("error when print");
        };

        var offset: usize = 0;
        while (offset < self.count) {
            offset = self.disassemble_instruction(offset) catch {
                @panic("error when print");
            };
        }
    }

    fn disassemble_instruction(self: *const Chunk, offset: usize) !usize {
        try common.stdout.print("{:0>4} ", .{offset});
        const instruction = self.code.?[offset];
        switch (@as(OpCode, @enumFromInt(instruction))) {
            .OP_RETURN => return simple_instruction("OP_RETURN", offset),

            .OP_CONSTANT => return constant_instruction("OP_CONSTANT", self, offset),

            else => {
                try common.stdout.print("Unknown opcode {d}\n", .{instruction});
                return offset + 1;
            },
        }
    }

    fn simple_instruction(name: []const u8, offset: usize) !usize {
        try common.stdout.print("{s}\n", .{name});
        return offset + 1;
    }

    fn constant_instruction(name: []const u8, chunk: *const Chunk, offset: usize) !usize {
        const constant_index = chunk.code.?[offset + 1];
        try common.stdout.print("{s} {:0>4} '", .{ name, constant_index });
        try print_value(chunk.constants.values.?[constant_index]);
        try common.stdout.print("'\n", .{});

        return offset + 2;
    }

    pub fn add_constant(self: *Chunk, value: Value) usize {
        self.constants.write_value_array(value);
        return self.constants.count - 1;
    }
};
